/**
 * KopsoxFramework
 * Copyright (C) 2010 Free Software Foundation, Inc. <http://fsf.org/>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kopsox.data.impl;

import org.kopsox.data.ValueDescriptor;
import org.kopsox.general.range.Range;
import org.kopsox.general.range.impl.IntegerRange;

/**
 * This is a immutable implementation of a ValueDescriptor<br>
 * The Descriptor is created with a builder
 * 
 * @author Konrad Renner - 21.06.2010
 *
 */
public final class ValueDescriptorImpl implements ValueDescriptor {
	
	private static final long serialVersionUID = -301907381149639804L;
	
	public static final String NO_NAME_VALUE = "noName";
	
	private final String name;
	private final String namespace;
	private final boolean isNullable;
	private final boolean isChangeable;
	private final boolean isHidden;
	private boolean isAutoValidationEnabled;
	private final IntegerRange rangeOfDigits;
	private final Range rangeOfValue;
	private boolean isAutoErrorDelegationEnabled;
	
	//Caches
	private int hashCode = 0;
	private String stringRepr = null;
	
	
	public ValueDescriptorImpl(ValueDescriptorBuilder builder){
		this.name = builder.name;
		this.isNullable = builder.isNullable;
		this.isChangeable = builder.isChangeable;
		this.isAutoValidationEnabled = builder.isAutoValidationEnabled;
		this.namespace = builder.namespace;
		this.rangeOfDigits = builder.rangeOfDigits;
		this.rangeOfValue = builder.rangeOfValue;
		this.isHidden = builder.isHidden;
		this.isAutoErrorDelegationEnabled = builder.isAutoErrorDelegationEnabled;
	}
	
	
	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#isAutomaticErrorDelegationEnabled()
	 */
	@Override
	public boolean isAutomaticErrorDelegationEnabled() {
		return this.isAutoErrorDelegationEnabled;
	}
	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#setAutomaticErrorDelegation(boolean)
	 */
	@Override
	public void setAutomaticErrorDelegation(final boolean flag) {
		this.isAutoErrorDelegationEnabled = flag;
	}
	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#getRangeOfDigitsFromValue()
	 */
	@Override
	public IntegerRange getRangeOfDigitsFromValue() {
		return this.rangeOfDigits;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#getRangeOfValue()
	 */
	@Override
	public Range getRangeOfValue() {
		return this.rangeOfValue;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#setAutoValidationEnabled()
	 */
	@Override
	public void setAutoValidationEnabled(final boolean autoVal) {
		this.isAutoValidationEnabled = autoVal;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#getName()
	 */
	@Override
	public String getName() {
		return this.name;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#getNamespace()
	 */
	@Override
	public String getNamespace() {
		return this.namespace;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#isChangeable()
	 */
	@Override
	public boolean isChangeable() {
		return this.isChangeable;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#isNullable()
	 */
	@Override
	public boolean isNullable() {
		return this.isNullable;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#isAutoValidationEnabled()
	 */
	@Override
	public boolean isAutoValidationEnabled() {
		return this.isAutoValidationEnabled;
	}
	
	/* (non-Javadoc)
	 * @see org.kopsox.data.ValueDescriptor#isHidden()
	 */
	@Override
	public boolean isHidden() {
		return this.isHidden;
	}


	@Override
	public int hashCode() {
		int result = this.hashCode;
		if(result == 0) {
			result = 17;
			final int prime = 31;
			//result = prime * result + (isAutoValidationEnabled ? 1231 : 1237);
			result = prime * result + (isChangeable ? 1231 : 1237);
			result = prime * result + (isNullable ? 1231 : 1237);
			result = prime * result + (isHidden ? 1231 : 1237);
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
			result = prime * result + ((rangeOfValue == null) ? 0 : rangeOfValue.hashCode());
			result = prime * result + ((rangeOfDigits == null) ? 0 : rangeOfDigits.hashCode());
			
			hashCode = result;
		}
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ValueDescriptorImpl other = (ValueDescriptorImpl) obj;
		//if (isAutoValidationEnabled != other.isAutoValidationEnabled)
		//	return false;
		if (isChangeable != other.isChangeable)
			return false;
		if (isNullable != other.isNullable)
			return false;
		if (isHidden != other.isHidden)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (namespace == null) {
			if (other.namespace != null)
				return false;
		} else if (!namespace.equals(other.namespace))
			return false;
		if (rangeOfValue == null) {
			if (other.rangeOfValue != null)
				return false;
		} else if (!rangeOfValue.equals(other.rangeOfValue))
			return false;
		if (rangeOfDigits == null) {
			if (other.rangeOfDigits != null)
				return false;
		} else if (!rangeOfDigits.equals(other.rangeOfDigits))
			return false;
		return true;
	}
	
	@Override
	public String toString(){
		if(stringRepr == null) {
			StringBuilder sb = new StringBuilder();
			sb.append("ValueDescriptorImpl:{");
			sb.append("name=");
			sb.append(this.name);
			sb.append(",namespace:");
			sb.append(this.namespace);
			sb.append(",isAutoValidationEnabled:");
			sb.append(this.isAutoValidationEnabled);
			sb.append(",isAutoErrorDelegationEnabled:");
			sb.append(this.isAutoErrorDelegationEnabled);
			sb.append(",isChangeable:");
			sb.append(this.isChangeable);
			sb.append(",isNullable:");
			sb.append(this.isNullable);
			sb.append(",isHidden:");
			sb.append(this.isHidden);
			sb.append(",rangeOfValue:");
			sb.append(this.rangeOfValue);
			sb.append(",rangeOfDigits:");
			sb.append(this.rangeOfDigits);
			sb.append('}');
			
			stringRepr = sb.toString();
		}

		return stringRepr;
	}
	
	//Builder
	public static final class ValueDescriptorBuilder{
		//Required parameter
		protected final String name;
		
		//Optional parameter
		protected String namespace = null;
		protected boolean isHidden = false;
		protected boolean isNullable = true;
		protected boolean isChangeable = true;
		protected boolean isAutoValidationEnabled = true;
		protected Range rangeOfValue = null;
		protected IntegerRange rangeOfDigits = null;
		protected boolean isAutoErrorDelegationEnabled = true;
		
		public ValueDescriptorBuilder() {
			this(NO_NAME_VALUE);
		}
		
		public ValueDescriptorBuilder(final String name) {
			if(name == null) {
				this.name = NO_NAME_VALUE;
			}else {
				this.name = name;
			}
		}
		
		public ValueDescriptorBuilder namespace(final String nameSpace) {
			this.namespace = nameSpace;
			return this;
		}
		
		public ValueDescriptorBuilder isNullable(final boolean nullable) {
			this.isNullable = nullable;
			return this;
		}
		
		public ValueDescriptorBuilder isHidden(final boolean hidden) {
			this.isHidden = hidden;
			return this;
		}
		
		public ValueDescriptorBuilder isChangeable(final boolean changeable) {
			this.isChangeable = changeable;
			return this;
		}
		
		public ValueDescriptorBuilder isAutoValidationEnabled(final boolean autoValidationEnabled) {
			this.isAutoValidationEnabled = autoValidationEnabled;
			return this;
		}
		
		public ValueDescriptorBuilder isAutoErrorDelegationEnabled(final boolean flag) {
			this.isAutoErrorDelegationEnabled = flag;
			return this;
		}
		
		public ValueDescriptorBuilder rangeOfDigits(final IntegerRange range) {
			this.rangeOfDigits = range;
			return this;
		}
		
		public ValueDescriptorBuilder rangeOfValue(final Range range) {
			this.rangeOfValue = range;
			return this;
		}
		
		public ValueDescriptorImpl build() {
			return new ValueDescriptorImpl(this);
		}
	}
}
